package cn.bugstack.chatglm.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description 签名工具包；过期时间30分钟，缓存时间29分钟
 * 提供的 Cache 和 ConcurrentMap 很相似，但也不完全一样。
 * 最基本的区别是 ConcurrentMap 会一直保存所有添加的元素，直到显式地移除。相对地，我提供的 Cache 为了限制内存占用，通常都设定为自动回收元素。
 * 如果你愿意消耗一些内存空间来提升速度，你能预料到某些键会被查询一次以上，缓存中存放的数据总量不会超出内存容量，就可以使用 Cache。
 */
@Slf4j
public class BearerTokenUtils {

    // 过期时间；默认30分钟
    private static final long expireMillis = 30 * 60 * 1000L;

    // 缓存服务，这里使用到Guava，是google开源的Java工具库，提供集合、字符串、缓存等很多处理工具
    public static Cache<String, String> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(expireMillis - (60 * 1000L), TimeUnit.MILLISECONDS)//设置29分钟过期，减少每次都需要创建的开销
            .build();

    /**
     * 对 ApiKey 进行签名（创建token）
     * @param apiKey    登录创建 ApiKey <a href="https://open.bigmodel.cn/usercenter/apikeys">apikeys</a>
     * @param apiSecret apiKey的后半部分 828902ec516c45307619708d3e780ae1.w5eKiLvhnLP8MtIf 取 w5eKiLvhnLP8MtIf 使用
     * @return Token
     */
    public static String getToken(String apiKey, String apiSecret) {
        // 先尝试从缓存获取Token，因为每根据API-key创建一个token，都会放入缓存中，29min内一直有效
        String token = cache.getIfPresent(apiKey);
        if (null != token) return token;
        // 创建Token
        //使用HMAC256算法加密，apiSecret 是用于生成签名的密钥，将其转换为字节数组后传入 HMAC SHA-256（HS256） 算法中。
        Algorithm algorithm = Algorithm.HMAC256(apiSecret.getBytes(StandardCharsets.UTF_8));
        //构建token的payload，包含需要携带的实际信息
        Map<String, Object> payload = new HashMap<>();
        payload.put("api_key", apiKey);
        payload.put("exp", System.currentTimeMillis() + expireMillis);
        payload.put("timestamp", Calendar.getInstance().getTimeInMillis());
        //构建token的header
        Map<String, Object> headerClaims = new HashMap<>();
        headerClaims.put("alg", "HS256");//指定加密算法 HMAC SHA-256（HS256）
        headerClaims.put("sign_type", "SIGN");
        //生成token
        token = JWT.create().withPayload(payload).withHeader(headerClaims).sign(algorithm);
        //将生成的 token 存入缓存中，缓存的键是 apiKey，这样以后同一个 apiKey 就可以直接从缓存中获取 token，避免每次都重新计算
        cache.put(apiKey, token);
        return token;
    }

}
